/*
 * Copyright (C) 2016  Nicola Spanti (RyDroid) <dev@nicola-spanti.info>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */


/**
 * @file
 * @brief A similar implementation of std::vector of C++ in C
 */


#ifndef PLANET_WARS_2D_RT_CORE_VECTOR_GENERIC_H
#define PLANET_WARS_2D_RT_CORE_VECTOR_GENERIC_H


#include "PlanetWars2dRT-core/utils/c_language.h"
#include "PlanetWars2dRT-core/utils/bool.h"
#include <stdlib.h>


#define DEFINE_VECTOR_GENERIC_STATIC(type)	\
  typedef struct vector_##type vector_##type;	\
  struct vector_##type				\
  {						\
    type* data;					\
    size_t size_current;			\
    size_t size_allocated;			\
  };

#define VECTOR_GENERIC_STATIC_INIT_FIRST_TIME_ONLY(vector, size, type)	\
  do									\
    {									\
      (vector)->size_current   = 0;					\
      (vector)->size_allocated = size;					\
      if(size <= 0)							\
	{								\
	  (vector)->data = NULL;					\
	}								\
      else								\
	{								\
	  (vector)->data = (type*) malloc(size * sizeof(type));		\
	}								\
    }									\
  while(false);

#define VECTOR_GENERIC_STATIC_INIT_FIRST_TIME_ONLY_DEFAULT(vector, type) \
  VECTOR_GENERIC_STATIC_INIT_FIRST_TIME_ONLY(vector, 2, type)

#define VECTOR_GENERIC_STATIC_INIT_FIRST_TIME_ONLY_NULL(vector, size, type) \
  do									\
    {									\
      (vector)->size_current   = size;					\
      (vector)->size_allocated = size;					\
      if(size <= 0)							\
	{								\
	  (vector)->data = NULL;					\
	}								\
      else								\
	{								\
	  (vector)->data = (type*) calloc(size, sizeof(type));		\
	}								\
    }									\
  while(false);

#define VECTOR_GENERIC_STATIC_INIT_FIRST_TIME_ONLY_NULL_DEFAULT(vector, type) \
  VECTOR_GENERIC_STATIC_INIT_FIRST_TIME_ONLY_NULL(vector, 2, type)

#define VECTOR_GENERIC_STATIC_DESTRUCTOR(vector)	\
  if((vector)->data != NULL)				\
    {							\
      free((vector)->data);				\
    }


/* Capacity */

#define VECTOR_GENERIC_STATIC_IS_EMPTY_UNSAFE(vector)	\
  ((vector)->size_current == 0)

#define VECTOR_GENERIC_STATIC_IS_EMPTY(vector)	\
  ((vector) != NULL &&				\
   VECTOR_GENERIC_STATIC_IS_EMPTY_UNSAFE(vector))

#define VECTOR_GENERIC_STATIC_GET_SIZE_UNSAFE(vector)	\
  ((vector)->size_current)

#define VECTOR_GENERIC_STATIC_GET_SIZE(vector)	\
  ((vector) == NULL				\
   ? 0						\
   : VECTOR_GENERIC_STATIC_GET_SIZE_UNSAFE(vector))

#define VECTOR_GENERIC_STATIC_GET_SIZE_ALLOCATED_UNSAFE(vector)	\
  ((vector)->size_allocated)

#define VECTOR_GENERIC_STATIC_GET_SIZE_ALLOCATED(vector)	\
  ((vector) == NULL						\
   ? 0								\
   : VECTOR_GENERIC_STATIC_GET_SIZE_ALLOCATED_UNSAFE(vector))

#define VECTOR_GENERIC_STATIC_ALLOCATE(vector, size, type)		\
  if((vector)->size_current < size)					\
    {									\
      (vector)->data = (type *) realloc((vector)->data,			\
					size * sizeof(type));		\
      (vector)->size_allocated = size;					\
    }

#define VECTOR_GENERIC_STATIC_RESIZE(vector, size, type)		\
  if((vector)->size_current > size)					\
    {									\
      (vector)->size_current = size;					\
    }									\
  else if((vector)->size_current < size)				\
    {									\
      (vector)->data = (type *) realloc((vector)->data,			\
					size * sizeof(type));		\
      (vector)->size_current   = size;					\
      (vector)->size_allocated = size;					\
    }

/**
 * @brief Unused allocated memory is free.
 * So allocated size becomes equal to current size
 */
#define VECTOR_GENERIC_STATIC_SHRINK_TO_FIT(vector, type)		\
  VECTOR_GENERIC_STATIC_RESIZE(vector, (vector)->size_current, type)


/* Element access */

#define VECTOR_GENERIC_STATIC_GET_ITEM_POINTER_UNSAFE(vector, index)	\
  ((vector)->data + index)

#define VECTOR_GENERIC_STATIC_GET_ITEM_POINTER(vector, index)	\
  (((vector)->data == NULL)					\
   ? NULL							\
   : VECTOR_GENERIC_STATIC_GET_ITEM_POINTER_UNSAFE(vector, index))

#define VECTOR_GENERIC_STATIC_GET_ITEM_VALUE_UNSAFE(vector, index)	\
  (* VECTOR_GENERIC_STATIC_GET_ITEM_POINTER_UNSAFE(vector, index))

/**
 * @brief Returns an iterator to beginning
 */
#define VECTOR_GENERIC_STATIC_GET_BEGIN_POINTER(vector)		\
  VECTOR_GENERIC_STATIC_GET_ITEM_POINTER(vector, 0)

/**
 * @brief Returns an iterator to the element past the end
 */
#define VECTOR_GENERIC_STATIC_GET_END_POINTER(vector)		\
  VECTOR_GENERIC_STATIC_GET_ITEM_POINTER(vector, (vector)->size_current)

#define VECTOR_GENERIC_STATIC_GET_BEGIN_VALUE(vector)		\
  VECTOR_GENERIC_STATIC_GET_ITEM_VALUE(vector, 0)

#define VECTOR_GENERIC_STATIC_GET_END_VALUE(vector)		\
  VECTOR_GENERIC_STATIC_GET_ITEM_VALUE(vector, (vector)->size_current - 1)

#define VECTOR_GENERIC_STATIC_GET_DATA_UNSAFE(vector)		\
  ((vector)->data)

#define VECTOR_GENERIC_STATIC_GET_DATA(vector)		\
  ((vector) == NULL					\
   ? 0							\
   : VECTOR_GENERIC_STATIC_GET_DATA_UNSAFE(vector))


/* Modifiers */

#define VECTOR_GENERIC_STATIC_SET_ITEM_VALUE_UNSAFE(vector, index, value) \
  (* VECTOR_GENERIC_STATIC_GET_ITEM_POINTER_UNSAFE(vector, index) = (value))

/**
 * @brief Remove all elements of the vector
 */
#define VECTOR_GENERIC_STATIC_CLEAR(vector)	\
  if((vector)->data != NULL)			\
    {						\
      free((vector)->data);			\
      (vector)->data = NULL;			\
      (vector)->size_current   = 0;		\
      (vector)->size_allocated = 0;		\
    }

/**
 * @brief Add a value at the end of the vector and increment current size
 */
#define VECTOR_GENERIC_STATIC_PUSH_BACK(vector, value, type)		\
  do									\
    {									\
      if((vector)->size_current == 0)					\
	{								\
	  VECTOR_GENERIC_STATIC_ALLOCATE(vector,			\
					 2,				\
					 type);				\
	}								\
      else if((vector)->size_current == (vector)->size_allocated)	\
	{								\
	  VECTOR_GENERIC_STATIC_ALLOCATE(vector,			\
					 (vector)->size_allocated * 2,	\
					 type);				\
	}								\
      VECTOR_GENERIC_STATIC_SET_ITEM_VALUE_UNSAFE(vector,		\
						  (vector)->size_current, \
						  value);		\
      ++(vector)->size_current;						\
    }									\
  while(false);

/**
 * @brief Remove the last element if there is one.
 */
#define VECTOR_GENERIC_STATIC_POP_BACK(vector)	\
  if((vector)->size_current > 0)		\
    {						\
      --(vector)->size_current;			\
    }


#endif
